import hmac
import json
import requests
import urllib.parse as urlparse
import hashlib
import sys
import time

from coin.exchange.base.kr_rest.auth_base import AuthKey
from coin2.api_client.common import (SyncQueryer)
from enum import Enum

class Side(Enum):
  BUY = "buy"
  SELL = "sell"

class Type(Enum):
  LIMIT = "limit"
  MARKET = "market"
  STOP = "stop"
  STOP_LIMIT = "stop_limit"

class BitbankAuth(requests.auth.AuthBase):
  def __init__(self, key):
    self._key = AuthKey.from_file(key)

  def _gen_signature(self, nonce: str, method: str, path_with_query: str, body: str):
    """
    https://github.com/bitbankinc/bitbank-api-docs/blob/master/rest-api.md#authorization

    ACCESS-SIGNATURE : Hash the following string with HMAC-SHA256, using your API secret as hash key.
      GET: [ACCESS-NONCE, full request path with query parameters] concatenated (include /v1 in request path).
      POST: [ACCESS-NONCE, JSON string of request body] concatenated (include query parameters in request body).
    """
    assert nonce, "nonce must be not None and not empty!"

    join_list = []

    if method == 'GET':
      join_list = [nonce, path_with_query]
    elif method == 'POST':
      join_list = [nonce, body]
    else:
      assert False, "ACCESS-SIGNATURE only support method GET and POST!"

    before_signature = ''.join(filter(None, join_list)).encode()

    signature = hmac.new(self._key.secret_key_bytes, before_signature, digestmod=hashlib.sha256)
    return signature.hexdigest()

  def __call__(self, req: requests.PreparedRequest):
    # nonce in microsecond
    nonce = str(int(time.time() * 1000 * 1000))
    parsed = urlparse.urlparse(req.url)
    body = req.body
    if body != None and isinstance(body, bytes):
      body = body.decode()
    path_with_query = parsed.path
    if parsed.query:
      path_with_query = path_with_query + "?" + parsed.query

    req.headers.update({'ACCESS-KEY': self._key.access_key})
    req.headers.update({'ACCESS-NONCE': nonce})

    signature = self._gen_signature(nonce, req.method, path_with_query, body)
    req.headers.update({'ACCESS-SIGNATURE': signature})

    req.prepare_headers(req.headers)

    # print(req.headers)
    # print(req.body)
    # print(req.url)

    return req

class BitbankPrivateClient():
  def __init__(self, key_path: str, ip=None):
    self._auth = BitbankAuth(key_path)
    self._url = "https://api.bitbank.cc"

    self._query = SyncQueryer(api_host=self._url, auth=self._auth, ip=ip)

  def get_open_order(self, pair: str):
    assert pair, "pair must be not None and not empty!"

    method = "GET"
    params = {
      'pair': pair,
    }
    path = "/v1/user/spot/active_orders"

    response = self._query.query(method=method, path=path, params=params)
    return response.json()

  def get_balance(self):
    method = "GET"
    params = {
    }
    path = "/v1/user/assets"

    response = self._query.query(method=method, path=path, params=params)
    return response.json()

  def submit_order(self, pair: str, amount: str, price: str, side: Side, type: Type):
    assert pair, "pair must be not None and not empty!"
    assert amount, "amount must be not None and not empty!"
    assert side, "side must be not None!"
    assert type, "type must be not None!"

    method = "POST"
    json = {
      'pair': pair,
      'amount': amount,
      'price': price,
      'side': side.value,
      'type': type.value,
    }
    path = "/v1/user/spot/order"

    response = self._query.query(method=method, path=path, json=json)
    return response.json()

  def cancel_order(self, pair: str, order_id: str):
    assert pair, "pair must be not None and not empty!"
    assert order_id, "order_id must be not None and not empty!"

    method = "POST"
    json = {
      'pair': pair,
      'order_id': order_id,
    }
    path = "/v1/user/spot/cancel_order"

    response = self._query.query(method=method, path=path, json=json)
    return response.json()

  def get_order_info(self, pair: str, order_id: str):
    assert pair, "pair must be not None and not empty!"
    assert order_id, "order_id must be not None and not empty!"

    method = "GET"
    params = {
      'pair': pair,
      'order_id': order_id,
    }
    path = "/v1/user/spot/order"

    response = self._query.query(method=method, path=path, params=params)
    return response.json()

  def get_trade_history(self, pair: str):
    assert pair, "pair must be not None and not empty!"

    method = "GET"
    params = {
      'pair': pair,
    }
    path = "/v1/user/spot/trade_history"

    response = self._query.query(method=method, path=path, params=params)
    return response.json()

# api doc url
# https://github.com/bitbankinc/bitbank-api-docs

if __name__ == "__main__":
  key_path = sys.argv[1]
  
  bitbank = BitbankPrivateClient(key_path)
  info = bitbank.get_open_order("btc_jpy")
  # info = bitbank.get_balance()
  # info = bitbank.cancel_order("btc_jpy", "123413")
  # info = bitbank.submit_order("btc_jpy", "0.00000001", "100000000", Side.SELL, Type.LIMIT)
  print(json.dumps(info, indent=2))
